/*
 * Copyright 2025 LinQingYing. and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of this source code is governed by the Apache License 2.0,
 * which allows users to freely use, modify, and distribute the code,
 * provided they adhere to the terms of the license.
 *
 * The software is provided "as-is", and the authors are not responsible for
 * any damages or issues arising from its use.
 *
 */

package org.cangnova.cangjie.parsing

import org.cangnova.cangjie.lang.CangJieFileType
import org.cangnova.cangjie.lang.CangJieLanguage
import org.cangnova.cangjie.lang.declarations.CangJieDeclarationsFileType
import org.cangnova.cangjie.lang.declarations.CjDeclarationsFile
import org.cangnova.cangjie.lexer.CangJieLexer
import org.cangnova.cangjie.lexer.CjToken
import org.cangnova.cangjie.lexer.CjTokens
import org.cangnova.cangjie.macro.file.CangJieMacroCallFileType
import org.cangnova.cangjie.macro.file.CjMacroCallFile
import org.cangnova.cangjie.psi.CjFile
import org.cangnova.cangjie.psi.CjNodeType
import org.cangnova.cangjie.psi.CjNodeTypes
import org.cangnova.cangjie.lexer.cdoc.lexer.CDocTokens
import org.cangnova.cangjie.lexer.cdoc.parser.CDocElementType
import org.cangnova.cangjie.lexer.cdoc.psi.impl.CDocLink
import org.cangnova.cangjie.psi.stubs.elements.CjFileElementType
import org.cangnova.cangjie.psi.stubs.elements.CjStubElementType
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.lang.LanguageParserDefinitions
import com.intellij.lang.ParserDefinition
import com.intellij.lang.PsiParser
import com.intellij.lexer.Lexer
import com.intellij.openapi.project.Project
import com.intellij.psi.FileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.PsiPlainTextFileImpl
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.IFileElementType
import com.intellij.psi.tree.TokenSet

class CangJieParserDefinition : ParserDefinition {
    override fun createLexer(project: Project?): Lexer = CangJieLexer()

    override fun createParser(project: Project?): PsiParser = CangJieParser(project!!)


    override fun getFileNodeType(): IFileElementType = CjFileElementType.INSTANCE

    val EOL_COMMENT = CjToken("EOL_COMMENT", 4)
    val BLOCK_COMMENT = CjToken("BLOCK_COMMENT", 3)
    val DOC_COMMENT: IElementType = CDocTokens.CDOC

    val SHEBANG_COMMENT = CjToken("SHEBANG_COMMENT", 5)
    val COMMENTS = TokenSet.create(
        EOL_COMMENT,
        BLOCK_COMMENT,
        DOC_COMMENT,
        SHEBANG_COMMENT,
    )

    override fun getCommentTokens(): TokenSet = CjTokens.COMMENTS

    override fun getWhitespaceTokens(): TokenSet = CjTokens.WHITESPACES

    override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY

    override fun createElement(astNode: ASTNode): PsiElement {
        return when (val elementType = astNode.elementType) {
            is CjStubElementType<*, *> -> elementType.createPsiFromAst(astNode)
            CjNodeTypes.TYPE_CODE_FRAGMENT, CjNodeTypes.EXPRESSION_CODE_FRAGMENT, CjNodeTypes.BLOCK_CODE_FRAGMENT -> ASTWrapperPsiElement(
                astNode,
            )

            is CDocElementType -> elementType.createPsi(astNode)
            CDocTokens.MARKDOWN_LINK -> CDocLink(astNode)
            else -> (elementType as CjNodeType).createPsi(astNode)
        }
    }

    override fun createFile(viewProvider: FileViewProvider): PsiFile {


        return when (viewProvider.fileType) {
            is CangJieDeclarationsFileType -> CjDeclarationsFile(viewProvider)
            is CangJieMacroCallFileType -> CjMacroCallFile(viewProvider)
            is CangJieFileType -> CjFile(viewProvider, false)
            else -> PsiPlainTextFileImpl(viewProvider)
        }


    }

    object Util {
        const val STD_SCRIPT_SUFFIX = "cj"

        @JvmField
        val STD_SCRIPT_EXT = ".$STD_SCRIPT_SUFFIX"
        val instance: CangJieParserDefinition
            get() = LanguageParserDefinitions.INSTANCE.forLanguage(CangJieLanguage) as CangJieParserDefinition
    }
}
